import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class PrepareSdCard {
    public static void main(String[] args) throws IOException, InterruptedException {
        Process process = Runtime.getRuntime().exec("df");
        if (!process.waitFor(15, TimeUnit.SECONDS)) {
            throw new RuntimeException("df fail");
        }
        String output = toString(process.getInputStream());
        AtomicInteger integer = new AtomicInteger(Integer.parseInt(args[0]));
        String[] lines = output.split("\n");
        for (String line : lines) {
            String[] pieces = line.split("[ ]+");
            if (pieces.length >= 9) {
                if (pieces[8].startsWith("/Volumes/")) {
                    String index = pieces[0]
                        .replaceAll("^/dev/disk", "")
                        .replaceAll("s1$", "");
                    String device = "/dev/disk" + index;
                    System.out.println("begin " + device);

                    unmount(device);
                    Process eraseDisk = Runtime.getRuntime().exec("diskutil eraseDisk FAT32 EMPTY" +
                        integer.get() + " MBRFormat " + device);
                    if (!eraseDisk.waitFor(15, TimeUnit.SECONDS)) {
                        throw new RuntimeException("eraseDisk " + device + " timeout");
                    }
                    if (eraseDisk.exitValue() != 0) {
                        throw new RuntimeException("eraseDisk " + device + " fail with " + eraseDisk.exitValue() +
                            " msg " + toString(eraseDisk.getInputStream()) +
                            " err " + toString(eraseDisk.getErrorStream())
                        );
                    }

                    unmount(device);
                    Process dd = new ProcessBuilder()
                        .command(List.of("dd", "if=/Users/eleme/Downloads/backup.img", "of=" + device, "bs=4096"))
                        .redirectOutput(ProcessBuilder.Redirect.DISCARD)
                        .start();
                    if (!dd.waitFor(40, TimeUnit.MINUTES)) {
                        throw new RuntimeException("dd " + device + " timeout");
                    }
                    if (dd.exitValue() != 0) {
                        throw new RuntimeException("eraseDisk " + device + " fail with " + eraseDisk.exitValue());
                    }

                    Thread.sleep(10000);

                    File f = new File("/Volumes/NO NAME/cmdline.txt");
                    String cmd = toString(new FileInputStream(f));
                    cmd = cmd.substring(0, cmd.lastIndexOf('\n'));
                    cmd += " ip=192.168.10." + integer.get() + '\n';
                    Process setIp = new ProcessBuilder(List.of("sh", "-c", "echo " + cmd))
                        .redirectOutput(ProcessBuilder.Redirect.to(f))
                        .start();
                    if (!setIp.waitFor(15, TimeUnit.SECONDS)) {
                        throw new RuntimeException("setIp " + device + " timeout");
                    }
                    if (setIp.exitValue() != 0) {
                        throw new RuntimeException("setIp " + device + " fail with " + setIp.exitValue() +
                            " msg " + toString(setIp.getInputStream()) +
                            " err " + toString(setIp.getErrorStream())
                        );
                    }

                    Process rename = new ProcessBuilder(List.of("diskutil", "rename", "NO NAME", "" + integer.get()))
                        .start();
                    if (!rename.waitFor(15, TimeUnit.SECONDS)) {
                        throw new RuntimeException("rename " + device + " timeout");
                    }
                    if (rename.exitValue() != 0) {
                        throw new RuntimeException("rename " + device + " fail with " + rename.exitValue() +
                            " msg " + toString(rename.getInputStream()) +
                            " err " + toString(rename.getErrorStream())
                        );
                    }

                    System.out.println("sd " + integer.getAndIncrement() + " finish");
                }
            }
        }
    }

    private static void unmount(String device) throws IOException, InterruptedException {
        Process unmount = Runtime.getRuntime().exec("diskutil unmountDisk " + device);
        if (!unmount.waitFor(15, TimeUnit.SECONDS)) {
            throw new RuntimeException("unmount " + device + " timeout");
        }
        if (unmount.exitValue() != 0) {
            throw new RuntimeException("unmount " + device + " fail with " + unmount.exitValue() +
                " msg " + toString(unmount.getInputStream()) +
                " err " + toString(unmount.getErrorStream())
            );
        }
    }

    public static String toString(final InputStream input) throws IOException {
        StringBuilder sw = new StringBuilder();
        copy(input, sw);
        return sw.toString();
    }

    private static void copy(final InputStream input, final StringBuilder output) throws IOException {
        final InputStreamReader in = new InputStreamReader(input, StandardCharsets.UTF_8);
        copyLarge(in, output);
    }

    private static void copyLarge(final Reader input, final StringBuilder output) throws IOException {
        copyLarge(input, output, new char[1024 * 4]);
    }

    private static void copyLarge(final Reader input, final StringBuilder output, final char[] buffer) throws IOException {
        int n;
        while (-1 != (n = input.read(buffer))) {
            output.append(buffer, 0, n);
        }
    }
}
